/*
* Copyright 2016 LINE Corporation
*
* LINE Corporation licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package com.linecorp.armeria.client.circuitbreaker;
import static org.assertj.core.api.Assertions.assertThat;
import static org.awaitility.Awaitility.await;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import org.junit.Test;
import com.google.common.base.Ticker;
import com.google.common.testing.FakeTicker;
public class SlidingWindowCounterTest {
private static final FakeTicker ticker = new FakeTicker();
@Test
public void testInitialState() {
SlidingWindowCounter counter = new SlidingWindowCounter(ticker, Duration.ofSeconds(10),
Duration.ofSeconds(1));
assertThat(counter.count()).isEqualTo(new EventCount(0, 0));
}
@Test
public void testOnSuccess() {
SlidingWindowCounter counter = new SlidingWindowCounter(ticker, Duration.ofSeconds(10),
Duration.ofSeconds(1));
assertThat(counter.onSuccess()).isEmpty();
ticker.advance(1, TimeUnit.SECONDS);
assertThat(counter.onFailure()).contains(new EventCount(1, 0));
assertThat(counter.count()).isEqualTo(new EventCount(1, 0));
}
@Test
public void testOnFailure() {
SlidingWindowCounter counter = new SlidingWindowCounter(ticker, Duration.ofSeconds(10),
Duration.ofSeconds(1));
assertThat(counter.onFailure()).isEmpty();
ticker.advance(1, TimeUnit.SECONDS);
assertThat(counter.onFailure()).contains(new EventCount(0, 1));
assertThat(counter.count()).isEqualTo(new EventCount(0, 1));
}
@Test
public void testTrim() {
SlidingWindowCounter counter = new SlidingWindowCounter(ticker, Duration.ofSeconds(10),
Duration.ofSeconds(1));
assertThat(counter.onSuccess()).isEmpty();
assertThat(counter.onFailure()).isEmpty();
ticker.advance(1, TimeUnit.SECONDS);
assertThat(counter.onFailure()).contains(new EventCount(1, 1));
assertThat(counter.count()).isEqualTo(new EventCount(1, 1));
ticker.advance(11, TimeUnit.SECONDS);
assertThat(counter.onFailure()).contains(new EventCount(0, 0));
assertThat(counter.count()).isEqualTo(new EventCount(0, 0));
}
@Test
public void testConcurrentAccess() throws InterruptedException {
SlidingWindowCounter counter = new SlidingWindowCounter(Ticker.systemTicker(), Duration.ofMinutes(5),
Duration.ofMillis(1));
int worker = 6;
int batch = 100000;
AtomicLong success = new AtomicLong();
AtomicLong failure = new AtomicLong();
CyclicBarrier barrier = new CyclicBarrier(worker);
List<Thread> threads = new ArrayList<>(worker);
for (int i = 0; i < worker; i++) {
Thread t = new Thread(() -> {
try {
barrier.await();
long s = 0;
long f = 0;
for (int j = 0; j < batch; j++) {
double r = ThreadLocalRandom.current().nextDouble();
if (r > 0.6) {
counter.onSuccess();
s++;
} else if (r > 0.2) {
counter.onFailure();
f++;
}
}
success.addAndGet(s);
failure.addAndGet(f);
} catch (Exception e) {
e.printStackTrace();
}
});
threads.add(t);
t.start();
}
for (Thread thread : threads) {
thread.join();
}
await().untilAsserted(() -> assertThat(counter.onFailure()).isPresent());
assertThat(counter.count()).isEqualTo(new EventCount(success.get(), failure.get()));
}
@Test
public void testLateBucket() {
SlidingWindowCounter counter = new SlidingWindowCounter(ticker, Duration.ofSeconds(10),
Duration.ofSeconds(1));
ticker.advance(-1, TimeUnit.SECONDS);
assertThat(counter.onSuccess()).isEmpty();
assertThat(counter.count()).isEqualTo(new EventCount(0, 0));
}
}